# -*- coding: utf-8 -*-
"""
函数分为：
1.Python内建函数
2.自定义函数
"""

# 定义函数
def greet_user(username):
    """显示简单的问候语"""
    print("Hello, " + username.title() + "!")
greet_user('jesse')

# 传递实参
def describe_pet(animal_type, pet_name):
    """显示宠物的信息"""
    print("\nI have a " + animal_type + ".")
    
    
# 使用位置实参
describe_pet('hamster', 'harry')
# 使用关键字实参
describe_pet(animal_type='hamster', pet_name='harry')

# 默认值
def describe_pet(pet_name, animal_type='dog'):
    """显示宠物的信息"""
    print("\nI have a " + animal_type + ".")
    print("My " + animal_type + "'s name is " + pet_name.title() + ".")
    #返回值
    return pet_name.title()

#可变参数和自定义参数
#*args 和**kwargs
#单*：可传入元组或列表
#双**：双星号代表以字典形式打开，传参对象为一个字典。 键为参数名，值为参数。


#如果要限制关键字参数的名字，就可以用命名关键字参数，例如，只接收city和job作为关键字参数。这种方式定义的函数如下：
def person(name, age, *, city, job):
    print(name, age, city, job)

#和自定义关键字参数**kw不同，命名关键字参数需要一个特殊分隔符*，*后面的参数被视为命名关键字参数。
#如果函数定义中已经有了一个可变参数，后面跟着的命名关键字参数就不再需要一个特殊分隔符*了：
def person(name, age, *args, city, job):
    print(name, age, args, city, job)

#命名关键字参数必须传入参数名，这和位置参数不同。如果没有传入参数名，调用将报错：
#参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
def person(name, age, city='Beijing', *args, momo ,**kw):
    print(name, age, city ,args, momo, kw)

#递归函数
#使用递归函数需要注意防止栈溢出。在计算机中，函数调用是通过栈（stack）这种数据结构实现的，每当进入一个函数调用，栈就会加一层栈帧，
# 每当函数返回，栈就会减一层栈帧。由于栈的大小不是无限的，所以，递归调用的次数过多，会导致栈溢出。可以试试fact(1000)：
def fact(n):    #递归表示计算一个数的阶乘：
    if n==1:
        return 1
    return n*fact(n - 1)

#解决递归调用栈溢出的方法是通过尾递归优化，事实上尾递归和循环的效果是一样的，所以，把循环看成是一种特殊的尾递归函数也是可以的。
#尾递归是指，在函数返回的时候，调用自身本身，并且，return语句不能包含表达式。这样，编译器或者解释器就可以把尾递归做优化，
# 使递归本身无论调用多少次，都只占用一个栈帧，不会出现栈溢出的情况。
#尾递归是递归的一种特殊形式，在尾递归函数中，递归调用是函数的最后一个操作，并且其返回值直接被当前函数返回，没有额外的计算。
# 也就是说，函数在进行递归调用时，不需要保留当前调用栈的上下文信息，因为递归调用的结果就是整个函数的最终结果。
#将上述阶乘函数改写为尾递归形式：
def factorial_tail(n, accumulator=1):  #尾递归表示计算一个数的阶乘：
    # 基本情况
    if n == 0:
        return accumulator
    # 递归情况
    else:
        return factorial_tail(n - 1, n * accumulator)
    

#匿名函数lamda
#定义：lambda 函数是一种小型的、一次性使用的函数，它可以在需要函数对象的地方快速定义并使用，无需使用 def 关键字来正式定义函数。
#语法：lambda 参数列表: 表达式

# 定义一个简单的 lambda 函数，用于计算两个数的和
add = lambda x, y: x + y
result = add(3, 5)
print(result)  # 输出: 8

#作为参数传递给高阶函数,如 map()、filter() 和 sorted() 等
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # 输出: [1, 4, 9, 16]

#回调函数
#在一些需要简单回调函数的场景中，使用 lambda 函数可以避免定义一个完整的命名函数，使代码更加简洁。
#优点：简洁性，临时性   。 缺点：可读性差，不易调试，功能有限。
import tkinter as tk
root = tk.Tk()  
# lambda: print("Button clicked!") 作为按钮点击事件的回调函数。            
button = tk.Button(root, text="Click me", command=lambda: print("Button clicked!"))
button.pack()
root.mainloop()

